﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Data;
using System.Transactions;
using Defect_Tracking_System.Helpers;

namespace Defect_Tracking_System.Models
{
    public class AccountManagement
    {
        private static AccountManagement instance;

        private Utilities Util = new Utilities();

        private AccountManagement()
        {
        }

        public static AccountManagement GetInstance()
        {
            if (instance == null)
            {
                instance = new AccountManagement();
            }
            return instance;
        } 

        /// <summary>
        /// add a new user to group
        /// </summary>
        /// <param name="Model">GroupMemberModel</param>
        public void AddUser(AddGroupMemberModel Model)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account Acc = new Account();
            Acc.AccountId = Guid.NewGuid();
            Acc.UserName = Model.UserName;
            Acc.Password = Util.Md5(Model.Password);
            Acc.Role = Model.Role;
            Acc.GroupId = Model.GroupId;
            Acc.CreatedDate = DateTime.Now;
            Acc.IsActive = Model.IsActive;
            Entities.Accounts.AddObject(Acc);

            Profile profile = new Profile();
            profile.ProfileId = Guid.NewGuid();
            profile.AccountId = Acc.AccountId;
            profile.FirstName = Model.FirstName;
            profile.LastName = Model.LastName;
            profile.Email = Model.Email;
            profile.Address = Model.Address;
            Entities.Profiles.AddObject(profile);

            Entities.SaveChanges();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="UserName"></param>
        /// <returns></returns>
        public EditGroupMemberModel GetGroupMemberModel(String UserName)
        {
            EditGroupMemberModel Model = new EditGroupMemberModel();

            Account account = GetUserByUserName(UserName);
            Profile profile = account.Profiles.FirstOrDefault();

            Model.UserName = account.UserName;  
            Model.Role = account.Role;
            Model.IsActive = account.IsActive;

            Model.FirstName = profile.FirstName;
            Model.LastName = profile.LastName;
            Model.Email = profile.Email;
            Model.Address = profile.Address;

            return Model;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Model"></param>
        public void EditUser(EditGroupMemberModel Model)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account Acc = Entities.Accounts.FirstOrDefault(a => a.UserName == Model.UserName);
            Boolean OldIsActive = Acc.IsActive;
            Profile profile = Acc.Profiles.FirstOrDefault();

            if (Model.Role != null)
            {
                Acc.Role = Model.Role;
            }
            if (Model.Role != null)
            {
                Acc.IsActive = Model.IsActive;
            }

            profile.FirstName = Model.FirstName;
            profile.LastName = Model.LastName;
            profile.Email = Model.Email;
            profile.Address = Model.Address;

            Entities.SaveChanges();

            if (OldIsActive != Model.IsActive)
            {
                EmailNotification.GetIntance().NotifyDisableEnableAccount(Model.UserName);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Acc"></param>
        public void DeleteUser(String UserName)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account Acc = Entities.Accounts.FirstOrDefault(a => a.UserName == UserName);

            var projects = Entities.Projects.Where(p => p.Creator == Acc.AccountId);

            if (projects.Count() > 0)
            {
                throw new Exception("This user is creator of some projects. You can't delete this user!");
            }

            var defects = Entities.Defects.Where(d => d.Creator == Acc.AccountId || d.AssignedMember == Acc.AccountId);

            if (defects.Count() > 0)
            {
                throw new Exception("This user is creator or assigned member of some defect. You can't delete this user!");
            }

            var Comments = Entities.Comments.Where(c => c.Creator == Acc.AccountId);

            foreach (Comment c in Comments.ToList())
            {
                Entities.Comments.DeleteObject(c);
            }

            var ProjectMembers = Entities.ProjectMembers.Where(pm => pm.AccountId == Acc.AccountId);

            foreach (ProjectMember pm in ProjectMembers.ToList())
            {
                Entities.ProjectMembers.DeleteObject(pm);
            }


            Profile profile = Acc.Profiles.FirstOrDefault();

            Entities.DeleteObject(profile);

            Entities.DeleteObject(Acc);

            Entities.SaveChanges();
        }

        public IQueryable<Account> SearchUser(String Keyword, string GroupAdminUserName)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account acc = GetUserByUserName(GroupAdminUserName);

            return from a in Entities.Accounts
                   where (a.UserName.Contains(Keyword) ||
                           a.Profiles.FirstOrDefault().Email.Contains(Keyword) ||
                           (a.Profiles.FirstOrDefault().FirstName + " " + a.Profiles.FirstOrDefault().LastName).Contains(Keyword)) &&
                           a.GroupId == acc.GroupId
                   orderby a.UserName
                   select a;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Model"></param>
        public void ChangePassword(ChangePasswordModel Model)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account account = Entities.Accounts.FirstOrDefault(a => a.UserName == Model.UserName);

            if (Util.Md5(Model.OldPassword) != account.Password)
            {
                throw new Exception("Old Password is wrong");
            }

            account.Password = Util.Md5(Model.NewPassword);

            Entities.SaveChanges();
        }

        public void ResetPassword(String Email)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account account = GetUserByEmail(Email);
            account = Entities.Accounts.FirstOrDefault(a => a.AccountId == account.AccountId);

            if (account == null)
            {
                throw new Exception("Email is not existed !");
            }
            string NewPassword = Util.RandomString(16);
            account.Password = Util.Md5(NewPassword);

            Entities.SaveChanges();

            EmailNotification.GetIntance().NotifyResetPassword(account.UserName, NewPassword);
        }

        /// <summary>
        /// get user by username
        /// </summary>
        /// <param name="UserName">username</param>
        /// <returns></returns>
        public Account GetUserByUserName(string UserName)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();
            return Entities.Accounts.FirstOrDefault(a => a.UserName == UserName);
        }

        /// <summary>
        /// get user by email
        /// </summary>
        /// <param name="Email">GetUserByEmail</param>
        /// <returns></returns>
        public Account GetUserByEmail(string Email)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();
            return Entities.Accounts.FirstOrDefault(a => a.Profiles.FirstOrDefault().Email == Email);
        }

        /// <summary>
        /// Register a account and a group
        /// </summary>
        /// <param name="model">a regiter model</param>
        public void Register(RegisterModel model)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Group gr = Entities.Groups.FirstOrDefault(g => g.GroupName == model.GroupName);
            if (gr != null)
            {
                throw new Exception("Group Name is exited");
            }

            Group group = new Group();
            group.GroupId = Guid.NewGuid();
            group.GroupName = model.GroupName;
            group.CreatedDate = DateTime.Now;
            group.IsActived = true;
            Entities.Groups.AddObject(group);

            Account account = new Account();
            account.AccountId = Guid.NewGuid();
            account.UserName = model.UserName;
            account.Password = Util.Md5(model.Password);
            account.Role = "Group Admin";
            account.CreatedDate = DateTime.Now;
            account.GroupId = group.GroupId;
            account.IsActive = true;
            Entities.Accounts.AddObject(account);

            Profile profile = new Profile();
            profile.ProfileId = Guid.NewGuid();
            profile.AccountId = account.AccountId;
            profile.FirstName = model.FirstName;
            profile.LastName = model.LastName;
            profile.Address = model.Address;
            profile.Email = model.Email;
            Entities.Profiles.AddObject(profile);

            Entities.SaveChanges();
        }

        /// <summary>
        /// Validate user for login
        /// </summary>
        /// <param name="UserName">User Name</param>
        /// <param name="Password">Password</param>
        /// <returns></returns>
        public bool ValidateUser(string UserName, string Password)
        {
            UserName = HttpUtility.HtmlEncode(UserName);
            Password = HttpUtility.HtmlEncode(Password);

            Account account = GetUserByUserName(UserName);

            if (account != null)
            {
                if (account.Password == Util.Md5(Password))
                    return true;
            }

            return false;
        }

        public IQueryable<Account> GetAccounts(string GroupAdminUserName)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();
            try
            {
                Account acc = GetUserByUserName(GroupAdminUserName);               

                var allaccounts = Entities.Accounts.Where(a => a.GroupId == acc.GroupId).OrderBy(a=> a.UserName);

                return allaccounts;
            }
            catch (Exception ex) 
            { 
                throw ex; 
            };
        }

        public EmailNotificationSettingsModel GetEmailNotificationSettingsModel(String UserName)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account Acc = GetUserByUserName(UserName);

            if (Acc == null)
            {
                throw new Exception("User is not exited");
            }

            EmailNotificationSettingsModel Model = new EmailNotificationSettingsModel();

            Model.UserName = UserName;
            Model.SelectedCases = GetEmailNotificationsSetings(UserName);
            Model.Cases = Entities.EmailNotificationCases.ToList();

            return Model;
        }

        public String[] GetEmailNotificationsSetings(String UserName)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();

            Account Acc = GetUserByUserName(UserName);

            if (Acc == null)
            {
                return new String[0];
            }

            var SelectedCases = from sc in Entities.EmailNotificationSettings
                                where sc.AccountId == Acc.AccountId
                                select sc.CaseId;

            return SelectedCases.ToArray();
        }

        public void ChangeEmailNoficationSettings(EmailNotificationSettingsModel Model)
        {
            DefectTrackingSystemEntities Entities = new DefectTrackingSystemEntities();
            Account Acc = GetUserByUserName(Model.UserName);

            if (Acc == null)
            {
                throw new Exception("User is not exited");
            }

            foreach (String str in Model.SelectedCases)
            {
                EmailNotificationCase Case = Entities.EmailNotificationCases.FirstOrDefault(c => c.CaseID == str);
                if (Case == null)
                {
                    throw new Exception("Selected Case is invalid!");
                }
            }

            var Settings = from S in Entities.EmailNotificationSettings
                           where S.AccountId == Acc.AccountId
                           select S;

            foreach (EmailNotificationSetting Setting in Settings)
            {
                Entities.EmailNotificationSettings.DeleteObject(Setting);
            }

            foreach (String str in Model.SelectedCases)
            {
                EmailNotificationSetting Setting = new EmailNotificationSetting();
                Setting.AccountId = Acc.AccountId;
                Setting.CaseId = str;

                Entities.EmailNotificationSettings.AddObject(Setting);
            }

            Entities.SaveChanges();
        }
    }

    #region Models
    public class ChangePasswordModel
    {
        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Old Password")]
        public string OldPassword { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("New password")]
        [RegularExpression(".{6,}", ErrorMessage = "Password must from 6 to 12 characters")]
        public string NewPassword { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Confirm new password")]
        public string ConfirmPassword { get; set; }
    }

    public class ForgotPasswordModel
    {
        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Email Address")]
        [RegularExpression(@"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", ErrorMessage = "Email is invalid")]
        public string Email { get; set; }
    }

    public class LogOnModel
    {
        [Required]
        [DisplayName("User name")]
        [RegularExpression("^[a-zA-Z0-9_-]{5,50}$", ErrorMessage = "User name is invalid")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        public string Password { get; set; }

        [DisplayName("Remember me?")]
        public bool RememberMe { get; set; }
    }

    public class RegisterModel
    {
        [Required]
        [DisplayName("First Name")]
        public string FirstName { get; set; }

        [Required]
        [DisplayName("Last Name")]
        public string LastName { get; set; }

        [Required]
        [DisplayName("User name")]
        [RegularExpression("^[a-zA-Z0-9_-]{5,50}$", ErrorMessage = "User name must be in range \"a-zA-Z0-9_-\" and have at least 5 characters ")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Email address")]
        [RegularExpression(@"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", ErrorMessage = "Email is invalid")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        [RegularExpression(".{6,}", ErrorMessage = "Password must from 6 to 12 characters")]
        public string Password { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Confirm password")]
        public string ConfirmPassword { get; set; }

        [DisplayName("Address")]
        public string Address { get; set; }

        [Required]
        [DisplayName("Group Name")]
        public string GroupName { get; set; }
    }

    public class AccountModel
    {
        public String AccountId { get; set; }

        [Required]
        [DisplayName("Profile Id")]
        public String ProfileId { get; set; }

        [DisplayName("First Name")]
        public String FirstName { get; set; }

        [Required]
        [DisplayName("Last Name")]
        public String LastName { get; set; }

        [Required]
        [DisplayName("User Name")]
        public String UserName { get; set; }

        [DisplayName("Address")]
        public String Address { get; set; }

        [DisplayName("Email")]
        public String Email { get; set; }

        [DisplayName("Role")]
        public String Role { get; set; }

        //[Required]
        //[DisplayName("IsAvtive")]
        //public String IsActive { get; set; }

        public String CreatedDate { get; set; }

        //public String GroupId { get; set; }
        public AccountModel()
        {
        }
    }

    public class AddGroupMemberModel
    {
        [Required]
        [DisplayName("First Name")]
        public string FirstName { get; set; }

        [Required]
        [DisplayName("Last Name")]
        public string LastName { get; set; }

        [Required]
        [DisplayName("User name")]
        [RegularExpression("^[a-zA-Z0-9_-]{5,50}$", ErrorMessage = "User name is invalid")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Email address")]
        [RegularExpression(@"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", ErrorMessage = "Email is invalid")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Password")]
        [RegularExpression(".{6,}", ErrorMessage = "Password must from 6 to 12 characters")]
        public string Password { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [DisplayName("Confirm password")]
        public string ConfirmPassword { get; set; }

        [DisplayName("Address")]
        public string Address { get; set; }

        [DisplayName("Role")]
        public string Role { get; set; }

        public SelectList Roles;

        [DisplayName("Is Active")]
        public bool IsActive { get; set; }

        public Guid GroupId { get; set; }

        private static String[] _roles = { "Project Admin", "User" };

        public AddGroupMemberModel()
        {
            Role = "User";
            Roles = new SelectList(_roles, Role);
        }
    }

    public class EditGroupMemberModel
    {
        [Required]
        [DisplayName("First Name")]
        public string FirstName { get; set; }

        [Required]
        [DisplayName("Last Name")]
        public string LastName { get; set; }

        [DisplayName("User name")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.EmailAddress)]
        [DisplayName("Email address")]
        [RegularExpression(@"^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$", ErrorMessage = "Email is invalid")]
        public string Email { get; set; }

        [DisplayName("Address")]
        public string Address { get; set; }

        [DisplayName("Role")]
        public string Role { get; set; }

        public SelectList Roles;

        [DisplayName("Is Active")]
        public bool IsActive { get; set; }

        private static String[] _roles = { "Project Admin", "User" };

        public EditGroupMemberModel()
        {
            Roles = new SelectList(_roles);
        }
    }

    public class EmailNotificationSettingsModel
    {
        public String UserName { get; set; }
        public String[] SelectedCases { get; set; }
        public List<EmailNotificationCase> Cases { get; set; }
    }
    #endregion
}